#include "stdafx.hpp"
#include "ColligereApp.hpp"
#include "ColligereAudioDatabaseLayout.hpp"
#include "ColligereAudioDatabaseHandler.hpp"
#include "ColligereSearchDialog.hpp"
#include "ColligereListCtrl.hpp"
#include "ColligereConfigHandler.hpp"
#include "ColligereUtils.hpp"

//////////////////////////////////////////////
// ColligereFieldComboBoxClientData
//////////////////////////////////////////////

col::FieldComboBoxClientData::FieldComboBoxClientData 
( const DatabaseTable &rTable, const Column &rColumn ) : 
mrTable ( rTable ), mrColumn ( rColumn )
{
}

col::FieldComboBoxClientData::~FieldComboBoxClientData() 
{ 
}

const col::DatabaseTable &col::FieldComboBoxClientData::GetTable() const 
{ 
	return mrTable; 
}

const col::Column &col::FieldComboBoxClientData::GetColumn() const 
{
	return mrColumn; 
}

//////////////////////////////////////////////
// ColligereSearchDialog
//////////////////////////////////////////////

IMPLEMENT_DYNAMIC_CLASS ( col::SearchDialog, wxDialog )

col::SearchDialog::SearchDialog ( DatabaseInterfaceBase *pParent ) : mpParentDatabase ( pParent )
{
	using namespace boost;

	wxASSERT ( mpParentDatabase );

	wxXmlResource::Get()->LoadDialog ( this, mpParentDatabase->GetLayout(), 
		"ID_Dialog_Search" );

	mpRegExCheckBox = XRCCTRL ( *this, "ID_CheckBox_UseRegEx", wxCheckBox );
	mpCaseSensCheckBox = XRCCTRL ( *this, "ID_CheckBox_CaseSensitive", wxCheckBox );

	ColligereListCtrl *results_ctrl = XRCCTRL ( *this, 
		"ID_ListCtrl_SearchResults", ColligereListCtrl );

	results_ctrl->InsertColumn ( 0, _("Artist"), wxLIST_FORMAT_LEFT, 
		results_ctrl->GetClientSize().GetWidth() / 2 );
	results_ctrl->InsertColumn ( 1, _("Album"), wxLIST_FORMAT_LEFT, 
		results_ctrl->GetClientSize().GetWidth() / 2 );

	wxComboBox *fields = XRCCTRL ( *this, "ID_ComboBox_Fields", wxComboBox );

	mCurTableList = DatabaseTableListHandler::Get().GetTableList 
		( mpParentDatabase->GetHandler()->GetTypeId() );

	wxASSERT ( !mCurTableList.empty() );

	fields->Append ( _("All Fields") );
	fields->Select(0);

	foreach ( const DatabaseTable &tb, mCurTableList )
	{
		foreach ( const Column &col, tb.GetColumns() )
		{
			if ( !col.IsInternalColumn() )
			{
				wxString name = col.GetName();
				name.Replace ( '_', " " );
				fields->Append ( str ( format ( "[%1%] %2%") % capitalize 
					( tb.GetName() ) % capitalize ( name.c_str() ) ),
					new FieldComboBoxClientData ( tb, col ) );
			}
		}
	}
	if ( GetSizer() )
	{
		GetSizer()->SetSizeHints ( this );
	}
	ConnectEventHandlers();
	LoadUserPreferences();
}

col::SearchDialog::~SearchDialog()
{
	SaveUserPreferences();
}

void col::SearchDialog::ConnectEventHandlers()
{
	Bind ( wxEVT_COMMAND_BUTTON_CLICKED,
		&SearchDialog::OnButtonSearch, XRCID ( "ID_Button_Search" ) );
	Bind ( wxEVT_COMMAND_CHECKBOX_CLICKED,
		&SearchDialog::OnUseRegExChecked, XRCID ( "ID_CheckBox_UseRegEx" ) );
	Bind ( wxEVT_COMMAND_LIST_ITEM_SELECTED,
		&SearchDialog::OnResultSelected, XRCID ( "ID_ListCtrl_SearchResults" ) );
	Bind ( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
		&SearchDialog::OnResultDeselected, XRCID ( "ID_ListCtrl_SearchResults" ) );
}

void col::SearchDialog::LoadUserPreferences()
{
	const wxString search_terms  
		( ConfigHandler::Get().GetValue ( ConfigVariable::SearchTerms ) );
	
	if ( !search_terms.empty() )
	{
		wxStringTokenizer tkz ( search_terms, ",");

		while ( tkz.HasMoreTokens() )
		{
			wxString val ( tkz.GetNextToken() );
			val.Trim().Trim ( false );
			XRCCTRL ( *this, "ID_ComboBox_SearchTerm", wxComboBox )->Append ( val );
		}
	}
	mpRegExCheckBox->SetValue ( ConfigHandler::Get().IsEnabled 
		( ConfigVariable::UseRegExSearchChecked ) ); 

	if ( mpRegExCheckBox->IsChecked() )
	{
		mpCaseSensCheckBox->Disable();
	}
	else
	{
		mpCaseSensCheckBox->SetValue ( ConfigHandler::Get().IsEnabled 
			( ConfigVariable::CaseSensitiveSearchChecked ) ); 
	}
}

void col::SearchDialog::SaveUserPreferences()
{
	wxComboBox *search_terms_ctrl = XRCCTRL ( *this, "ID_ComboBox_SearchTerm", wxComboBox );

	wxString flatten;

	for ( wxUint32 item = 0; item < search_terms_ctrl->GetCount(); ++item )
	{
		append_if ( flatten, ",", not1 ( is_empty() ) ); 
		flatten += search_terms_ctrl->GetString ( item );
	}
	ConfigHandler::Get().SetValue ( ConfigVariable::SearchTerms, flatten );

	ConfigHandler::Get().SetValue ( ConfigVariable::CaseSensitiveSearchChecked,
		mpCaseSensCheckBox->GetValue() );

	ConfigHandler::Get().SetValue ( ConfigVariable::UseRegExSearchChecked,
		mpRegExCheckBox->GetValue() );
}

///////////////////////////////////
// Event handlers
///////////////////////////////////

void col::SearchDialog::OnButtonSearch ( wxCommandEvent & )
{
	const wxString sterm ( XRCCTRL ( *this, "ID_ComboBox_SearchTerm",
		wxComboBox )->GetValue() );

	if ( sterm.empty() ) return;

	ColligereListCtrl *results_ctrl = XRCCTRL ( *this, 
		"ID_ListCtrl_SearchResults", ColligereListCtrl );

	results_ctrl->DeleteAllItems();

	DatabaseItemBaseDeque results;

	wxComboBox *fields = XRCCTRL ( *this, "ID_ComboBox_Fields", wxComboBox );

	const wxInt32 field_sel = fields->GetSelection();

	wxASSERT ( field_sel != wxNOT_FOUND );

	const FieldComboBoxClientData *field_cd =  dynamic_cast < const FieldComboBoxClientData * > 
		( fields->GetClientObject ( field_sel ) );

	if ( mpRegExCheckBox->IsChecked() )
	{
		const wxRegEx re ( sterm, wxRE_ADVANCED );

		if ( re.IsValid() )
		{
			if ( field_cd )
			{
				results = mpParentDatabase->GetHandler()->SearchFromColumn
					( re, field_cd->GetTable(), field_cd->GetColumn() );
			}
			else
			{
				results = mpParentDatabase->GetHandler()->Search 
					( sterm, DatabaseHandlerBase::SearchFlag::UseRegEx );
			}
		}
	}
	else
	{
		if ( field_cd )
		{
			results = mpParentDatabase->GetHandler()->SearchFromColumn
				( sterm, mpCaseSensCheckBox->IsChecked(), field_cd->GetTable(), 
				field_cd->GetColumn() );
		}
		else
		{
			results = mpParentDatabase->GetHandler()->Search ( sterm, 
				DatabaseHandlerBase::SearchFlag::CaseSensitive );
		}
	}
	try
	{
		foreach ( const DatabaseItemBasePtr item, results )
		{
			const Album &album = dynamic_cast < const Album & > ( *item.get() );
			const Artist &artist = dynamic_cast < const Artist & > ( album.GetParent() );

			const long indx = results_ctrl->InsertItem 
				( results_ctrl->GetItemCount(), artist.GetArtist() );

			mSearchResultData[indx] = item;

			results_ctrl->SetColumnText ( indx, 1, album.GetAlbum() );
		}
		if ( !results.empty() )
		{
			XRCCTRL ( *this, "ID_StaticText_ResultsFound", wxStaticText )->SetLabel
				( wxString::Format ( "Results Found: %d", results.size() ) );
			XRCCTRL ( *this, "ID_StaticText_ResultsFound", wxStaticText )->Show();
			this->Layout();
		}
	}
	catch ( std::bad_cast &rE )
	{
		wxFAIL_MSG ( rE.what() );
	}

	wxComboBox *search_term_ctrl = XRCCTRL ( *this, "ID_ComboBox_SearchTerm", wxComboBox );

	if ( search_term_ctrl->FindString ( sterm, true ) == wxNOT_FOUND )
	{
		const wxUint32 max_sterm_count = 10;

		if ( search_term_ctrl->GetCount() == max_sterm_count )
		{
			search_term_ctrl->Delete(0);
		}
		search_term_ctrl->Append ( sterm );
	}
}

void col::SearchDialog::OnUseRegExChecked ( wxCommandEvent &rEvt )
{
	mpCaseSensCheckBox->SetValue ( false );
	mpCaseSensCheckBox->Enable ( !rEvt.IsChecked() );
}

void col::SearchDialog::OnResultSelected ( wxListEvent &rEvt )
{
	if ( rEvt.GetIndex() != -1 )
	{
		const DatabaseItemBasePtr sel = mSearchResultData[rEvt.GetIndex()];
		mpParentDatabase->GetLayout()->SelectItem ( *sel.get(), true );
	}
}

void col::SearchDialog::OnResultDeselected ( wxListEvent & )
{
	mpParentDatabase->GetLayout()->UnselectActiveItem();
}